/**
 * \file  plugin_admin.h
 * \brief Implementation of Thesis Plugin.
 *
 * ASO: Antiviral Sequence Optimizer
 * <http://aso.googlecode.com/>
 * Copyright (C) 2010 Velez Ezequiel, Peralta Andres, FuDePAN
 *
 * This file is part of the ASO project.
 *
 * Contents:       Header file for ASO providing ThesisPlugin and ThesisPluginNode
 *                 clases.
 *
 * System:         ASO
 * Language:       C++
 *
 * Author:         Velez Ezequiel, Peralta Andres
 * E-Mail:
 *
 * ASO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ASO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ASO.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <mili/mili.h>
#include "libpluginaso/libpluginaso.h"

/*! Lista de clases de antivirales */
typedef std::list<AvClass> ClassAvList;

/*! Estructura usada para comunicar las Clases ThesisPlugin y ThesisPluginNode. */
struct MyArguments
{
    Avs availableAntivirals;    /*! antivirales disponibles */
    ClassAvList classAvList;    /*! clases de antivirales para combinatoria inicial*/
};

/*! variable para comunicar los argumentos entre ThesisPlugin y ThesisPluginNode*/
static MyArguments arg;

/*! función local que retorna una politica combinatoria de N listadores en paralelo */
static CombinatoryPolicy* get_init_combination_policy(const Avs& availableAntivirals, const ClassAvList& classAvList)
{
    ParallelCombinatory* combP = new ParallelCombinatory("parallel");
    mili::CAutonomousIterator<ClassAvList> it(classAvList);
    while (!it.end())
    {
        Avs out;
        filter(availableAntivirals, "class", *it, out);
        ListCombinatory* comb = new ListCombinatory("lista class " + mili::to_string(*it));
        combP->add_policy(comb, out, false);
        ++it;
    }
    return combP;
}

//---------------------------------------------------------------------------------------
/*! Implementación de nodos según Thesis Plugin */
class ThesisPluginNode: public TherapyNode
{
    EmptyCombinatory comb;
    Score partialTherapyScore;     /*! atributo auxiliar para calculo de scoring */
    AvClass lastVirFail;           /*! fallo virologico previo (en caso de haber) */

public:
    /*! Constructor */
    ThesisPluginNode(const biopp::PseudoNucSequence& sequence, TherapyNode* parent, const AntiviralSet& av, DistanceValue value, const size_t id, CombinatoryPolicy* comb)
        : TherapyNode(sequence, parent, av, value, id, comb),
          comb("empty"),
          lastVirFail(cComb)
    {
        if (parent != NULL)
            partialTherapyScore = parent->scoring() + value;
        else
            partialTherapyScore = value;
    }

    /*! Destructor */
    ~ThesisPluginNode()
    {}

    virtual ThesisPluginNode* clone() const;
    /*! Función que asigna puntaje a las terapias */
    virtual Score scoring() const;
    /*! Retorna true si la terapia se debe continuar, false de otro modo */
    virtual TherapyStatus continue_therapy() const;
    /*! Permite cambiar el combinador de AV en caso de fallo virologico */
    virtual CombinatoryPolicy* virologic_failure(const CombinationStatus& status);
};


ThesisPluginNode* ThesisPluginNode::clone() const
{
    return new ThesisPluginNode(sequence, parent, antivirals, value, id, combPolicy);
}

Score ThesisPluginNode::scoring() const
{
    return partialTherapyScore;
}

TherapyStatus ThesisPluginNode::continue_therapy() const
{
    return Continue;
}

CombinatoryPolicy* ThesisPluginNode::virologic_failure(const CombinationStatus& status)
{
    ClassAvList classList;

    if (false)//(lastVirFail == cComb)
    {
        if (status.name == "lista class cPI")
        {
            lastVirFail = cPI;
            mili::insert_into(classList, cNRTI);
            mili::insert_into(classList, cNNRTI);
        }
        else if (status.name == "lista class cNRTI")
        {
            lastVirFail = cNRTI;
            mili::insert_into(classList, cPI);
            mili::insert_into(classList, cNNRTI);
        }
        else if (status.name == "lista class cNNRTI")
        {
            lastVirFail = cNNRTI;
            mili::insert_into(classList, cPI);
            mili::insert_into(classList, cNRTI);
        }
        return get_init_combination_policy(::arg.availableAntivirals, classList);
    }
    else
        return &comb;
}

class ThesisPlugin: public BasePlugin//Plugin
{
public:
    /*! Costructor*/
    ThesisPlugin(Unplugged::LibraryHandler handle)
        : BasePlugin(handle),
          class1(),
          class2(),
          class3(),
          availableAntivirals(),
          policy(NULL)
    {}
    /*! Función que genera un nodo */
    virtual TherapyNode* create_node(const biopp::PseudoNucSequence& sequence, TherapyNode* parent, const AntiviralSet& av, DistanceValue value, const size_t id);
    /*! Función que retorna el valor minimo que puede tener una terapia */
    virtual Score get_min_scoring_value() const;
    /*! Retorna la politica para generar el árbol */
    virtual GenerationPolicy* get_generation_policy() const;
    /*! Retorna la lista de parametros a inicializar */
    virtual void get_params(ParameterList& params);
    /*! Retorna true si se valida la BD */
    virtual bool accepts_db(AVIterator avIt, AntiviralsNames& rejectedAntivirals);
    /*! Retorna el nombre del plugin */
    virtual const string& get_name() const;
    /*! valida una combinación de antivirales */
    virtual bool validate_combination(const AntiviralSet& antivirals) const;
    /*! Define la matriz de distancias. */
//    virtual void get_distance_matrix(DistanceMatrix& matrix) const;
    /*! Destructor */
    ~ThesisPlugin()
    {
        delete policy;
    }
private:
    static const string NAME;
    static const Score  MIN_SCORE_VALUE;
    static biopp::DistanceMatrix DISTMATRIX;
    AvClass class1;             /*! clase de antiviral para combinatoria inicial*/
    AvClass class2;             /*! clase de antiviral para combinatoria inicial*/
    AvClass class3;             /*! clase de antiviral para combinatoria inicial*/
    Avs availableAntivirals;    /*! antivirales disponibles */
    CombinatoryPolicy* policy;
};

EXPORT_PLUGIN(ThesisPlugin)
const string ThesisPlugin::NAME = "thesis plugin";
const Score  ThesisPlugin::MIN_SCORE_VALUE = 0.0f;
biopp::DistanceMatrix ThesisPlugin::DISTMATRIX;

/*! Creo el nodo de acuerdo a mis necesidades */
TherapyNode* ThesisPlugin::create_node(const biopp::PseudoNucSequence& sequence, TherapyNode* parent, const AntiviralSet& av, DistanceValue value, const size_t id)
{
    ::arg.availableAntivirals = availableAntivirals;
    mili::insert_into(::arg.classAvList, class1);
    mili::insert_into(::arg.classAvList, class2);
    mili::insert_into(::arg.classAvList, class3);

    if (parent != NULL)          // si no es raíz, uso la politica combinatoria del padre
        return new ThesisPluginNode(sequence, parent, av, value, id, parent->get_combination_policy());
    else
    {
        policy = ::get_init_combination_policy(availableAntivirals, ::arg.classAvList);
        return new ThesisPluginNode(sequence, parent, av, value, id, policy);
    }
}

Score ThesisPlugin::get_min_scoring_value() const
{
    return MIN_SCORE_VALUE;
}

GenerationPolicy* ThesisPlugin::get_generation_policy() const
{
    return new Depth;//BetterBeam(6);
}

void ThesisPlugin::get_params(ParameterList& params)
{
    /*! Se establecen los parametros para la politica combinatoria */
    mili::insert_into(params, new Parameter<AvClass>("Antiviral class1", "(cNNRTI, cNRTI, cPI)", class1));
    mili::insert_into(params, new Parameter<AvClass>("Antiviral class2", "(cNNRTI, cNRTI, cPI)", class2));
    mili::insert_into(params, new Parameter<AvClass>("Antiviral class3", "(cNNRTI, cNRTI, cPI)", class3));
}

/*! Pre: avIt esta posicionado en el primer elemento */
bool ThesisPlugin::accepts_db(AVIterator avIt, AntiviralsNames& /*rejectedAntivirals*/)
{
    while (!avIt.end())
    {
        mili::insert_into(availableAntivirals, *avIt);
        ++avIt;
    }
    return true;
}

const string& ThesisPlugin::get_name() const
{
    return NAME;
}

bool ThesisPlugin::validate_combination(const AntiviralSet& /*antivirals*/) const
{
    return true;
}
